import data

# formal_param: $<key>  real_param: value
def formal_param_to_real_param(deck: data.Deck, line):
    start_index = line.index(r'$')
    end_index = line.index(r'>')
    formal_param = line[start_index : end_index + 1]
    real_param = ""
    if deck.proc_func_flag:
        if formal_param in deck.formal_real_params.keys():
            real_param = deck.formal_real_params[formal_param]
        elif formal_param in deck.func_var_defines.keys():
            real_param = deck.func_var_defines[formal_param]
        else:
            return "", False
    else:
        if formal_param in deck.vars_define.keys():
            real_param = deck.vars_define[formal_param]
        else:
            print("error: can not find the definition of %s " % (formal_param))
            return "", False
        
    new_line = line[0 : start_index] + str(real_param) + line[end_index + 1 :]
    if r'$' in new_line:
        new_line, flag = formal_param_to_real_param(deck, new_line)
    return new_line, True


def decode_loop_param(param, deck: data.Deck):
    if param.isdecimal():
        return int(param), True
    if r'$' in param:
        new_param, flag = formal_param_to_real_param(deck, param)
        return eval(new_param), True
    return None, False


def decode_param(param, deck: data.Deck):
    if param.isdecimal():
        return int(param), True
    if param in deck.formal_real_params.keys():
        return int(deck.formal_real_params[param]), True
    elif param in deck.vars_define.keys():
        return int(deck.vars_define[param]), True
    return None, False


def proc_func(deck:data.Deck, func: data.Func, fp: data.FileParse):
    for op in func.ops:
        optype = op.OpType()
        func = check_proc_func(optype)
        func(deck, op, fp)


def proc_macro(deck: data.Deck, op: data.MacroOp, fp: data.FileParse):
    print(len(fp.funcs))
    opFunc = [f for f in fp.funcs if f.name == op.name]
    assert len(opFunc) == 1
    func = opFunc[0]
    if len(func.params) != len(op.params):
        return False

    deck.formal_real_params.clear()
    deck.formal_real_params = dict(zip(func.params, op.params))
    deck.proc_func_flag = True
    proc_func(deck, func, fp)
    deck.proc_func_flag = False
    return True

def proc_stat(deck: data.Deck, op: data.StatOp, fp:data.FileParse):
   for ele in op.stat:
       if (r'$') in ele:
           new_ele, flag = formal_param_to_real_param(deck, ele)
           deck.output.append(str(new_ele))
       else:
           deck.output.append(str(ele))
   return True


def proc_loop(deck: data.Deck, op:data.LoopOp, fp:data.FileParse):
    start, flag = decode_loop_param(op.start, deck)
    end, flag = decode_loop_param(op.end, deck)
    step, flag = decode_loop_param(op.step, deck)
    for x in range(start, end + 1, step):
        for sub_op in op.stat:
            optype = sub_op.OpType()
            func = check_proc_func(optype)
            func(deck, sub_op, fp)
    return True


def calc_logic(left, op, rihgt):
    if op == "==":
        return left == rihgt
    elif op == ">=":
        return left >= rihgt
    elif op == "<=":
        return left <= rihgt
    elif op == "<":
        return left < rihgt
    elif op == ">":
        return left > rihgt
    elif op == "!=":
        return left != rihgt
    else:
        print("Error: if operator %s is invalid!" % (op))
        return False


def proc_if(deck: data.Deck, op: data.IfOp, fp: data.FileParse):
    left = decode_param(op.lParam, deck)
    right = decode_param(op.rParam, deck)
    deal_ops = []
    if calc_logic(left, op.operator, right):
        deal_ops = op.stat
    else:
        deal_ops = op.elif_op.stat

    for sub_op in deal_ops:
        op_type = sub_op.OpType()
        func = check_proc_func(op_type)
        func(deck, sub_op, fp)
        

def proc_var(deck: data.Deck, op: data.VarOp, fp: data.FileParse):
    var_name = "$<" + op.name + ">"
    var_value = ""
    if op.value.isdigit():
        var_value = op.value
    else:
        var_value, flag = formal_param_to_real_param(deck, op.value)
        assert(flag, "var %s is not defined" % op.value)
    if deck.proc_func_flag:
        deck.func_var_defines[var_name] = var_value
    else:
        deck.vars_define[var_name] = var_value
    return True


def check_proc_func(type: data.Type):
    funcs = {
        data.Type.VAR: proc_var,
        data.Type.LOOP: proc_loop,
        data.Type.IF: proc_if,
        # data.Type.ELIF: proc_elif,
        data.Type.MACRO: proc_macro,
        data.Type.STAT: proc_stat
    }
    assert type in funcs.keys()
    return funcs[type]

